﻿using HmmmIDE.Syntax;
using ICSharpCode.AvalonEdit;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace HmmmIDE.Intellisense
{
    public class RegisterTracker
    {
        private TextEditor _hmmmEditor;
        internal List<Instruction> InstructionList;
        private Instruction[] _oldInstructionList;
        int _oldInstructionCount = 0;

        private static string _jumpPattern
        {
            get
            {
                return Instruction.jumpPattern;
            }
        }

        public RegisterTracker(TextEditor editor)
        {
            this._hmmmEditor = editor;
            _hmmmEditor.TextArea.TextEntered += TextArea_TextEntered;
            _hmmmEditor.Document.PropertyChanged += LineNumberChanged;
            this.InstructionList = new List<Instruction>();
        }

        #region Document I/O


        private void LineNumberChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if(e.PropertyName == "LineCount")
            {
                UpdateRegisterNumber();
            }
        }

        private void TextArea_TextEntered(object sender, EventArgs e)
        {
            UpdateRegisterNumber();
            
        }

        #endregion

        /// <summary>
        /// Update register line number
        /// </summary>
        internal void UpdateRegisterNumber()
        {
            _oldInstructionList = this.InstructionList.ToArray();  // old one for register links

            this.InstructionList = new List<Instruction>();
            var currentLine = _hmmmEditor.Document.GetLineByOffset(_hmmmEditor.CaretOffset);
            foreach (var line in _hmmmEditor.Document.Lines)
            {
                
                // TODO: WILL ADD SYNTAX ERROR NOTIFICATION IN THE FUTURE

                string text = _hmmmEditor.Document.GetText(line);
                bool isMatch = false;
                foreach (var kvp in SyntaxManager.CommandRegex) // syntax check
                {
                    if (Regex.IsMatch(text, kvp.Value))
                    {
                        isMatch = true;
                        break;
                    }
                }
                if (isMatch)
                {
                    InstructionList.Add(new Instruction(text, line.LineNumber));
                }
            }

            // check for invalid instruction number
            for (int i = 0; i < InstructionList.Count; i++)
            {
                InstructionList[i].InstructionNumber = i;
                var line = _hmmmEditor.Document.GetLineByNumber(InstructionList[i].LineNumber);
                _hmmmEditor.Document.Replace(line, InstructionList[i].Text);
            }

            

            foreach(var instruction in InstructionList)
            {
                if(instruction.ReferenceLineNumber != -1)
                {
                    if(instruction.ReferenceLineNumber < _oldInstructionList.Length)
                    {
                        int referenceNumber = instruction.ReferenceLineNumber;
                        if (InstructionList.Count <= referenceNumber)
                        {
                            // in this case lines are deleted
                            int lineChangeCount = InstructionList.Count - _oldInstructionList.Length;
                            referenceNumber += lineChangeCount;

                            // so if they are still pointing to the same line
                            if (InstructionList[referenceNumber].CompareTo(_oldInstructionList[instruction.ReferenceLineNumber]) == 0)
                            {
                                ChangeRefrenceumber(instruction.LineNumber);
                            }
                        }
                        else if (InstructionList[instruction.ReferenceLineNumber].CompareTo(_oldInstructionList[instruction.ReferenceLineNumber]) != 0)
                        {
                            ChangeRefrenceumber(instruction.LineNumber);
                        }
                    }
                }
            }




            _oldInstructionCount = InstructionList.Count;  // update old register count for future use
            _oldInstructionList = null;
        }


        private void ChangeRefrenceumber(int lineNumber)
        {
            // update the register link number
            Regex regex = new Regex(_jumpPattern);
            var currentLine = _hmmmEditor.Document.Lines[lineNumber - 1];
            string text = _hmmmEditor.Document.GetText(currentLine);
            if (text != "" && regex.IsMatch(text))
            {
                var splitResult = text.Split('#');
                string beforeComment = splitResult[0];
                string rest = "";
                if (splitResult.Length > 0 && beforeComment != "") { rest = text.Replace(beforeComment, ""); } else if (splitResult.Length > 0) { rest = text; }
                var result = Regex.Replace(beforeComment, @",?\s{1,}\d{1,}", new MatchEvaluator(ChangeValue));
                _hmmmEditor.Document.Replace(currentLine, result + rest);
            }
        }

        private string ChangeValue(Match match)
        {
            return Regex.Replace(match.Value, @"\d{1,}", new MatchEvaluator(Increment));
        }
        /// <summary>
        /// Handles the change in reference number. It will take care of the sign
        /// </summary>
        /// <param name="match">Regex match for any number</param>
        /// <returns>string from changed number</returns>
        private string Increment(Match match)
        {
            return (Convert.ToInt32(match.Value) + InstructionList.Count - _oldInstructionCount).ToString();
        }
    }





    //class LineColorizer : DocumentColorizingTransformer
    //{
    //    int lineNumber;

    //    public LineColorizer(int lineNumber)
    //    {
    //        if (lineNumber < 1)
    //            throw new ArgumentOutOfRangeException("lineNumber", lineNumber, "Line numbers are 1-based.");
    //        this.lineNumber = lineNumber;
    //    }

    //    public int LineNumber
    //    {
    //        get { return lineNumber; }
    //        set
    //        {
    //            if (value < 1)
    //                throw new ArgumentOutOfRangeException("value", value, "Line numbers are 1-based.");
    //            lineNumber = value;
    //        }
    //    }

    //    protected override void ColorizeLine(ICSharpCode.AvalonEdit.Document.DocumentLine line)
    //    {
    //        if (!line.IsDeleted && line.LineNumber == lineNumber)
    //        {
    //            ChangeLinePart(line.Offset, line.EndOffset, ApplyChanges);
    //        }
    //    }

    //    void ApplyChanges(VisualLineElement element)
    //    {
    //        // apply changes here
    //        element.TextRunProperties.SetForegroundBrush(Brushes.Red);
    //    }
    //}

}
